package com.petdog.module.auth.service.impl;

import com.petdog.common.dto.ApiResponse;
import com.petdog.common.service.LoginAttemptService;
import com.petdog.common.util.JwtUtil;
import com.petdog.infrastructure.entity.User;
import com.petdog.infrastructure.entity.UserCommunity;
import com.petdog.infrastructure.mapper.CommunityMapper;
import com.petdog.infrastructure.mapper.UserCommunityMapper;
import com.petdog.infrastructure.mapper.UserMapper;
import com.petdog.module.auth.dto.CodeLoginRequest;
import com.petdog.module.auth.dto.LoginRequest;
import com.petdog.module.auth.dto.LoginResponse;
import com.petdog.module.auth.dto.RegisterRequest;
import com.petdog.module.auth.dto.SendVerificationCodeRequest;
import com.petdog.module.auth.dto.TokenRefreshRequest;
import com.petdog.module.auth.service.AuthService;
import com.petdog.module.user.service.UserService;
import com.petdog.common.service.VerificationCodeService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.mail.MessagingException;

import java.time.LocalDateTime;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;

import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;

/**
 * 认证服务实现类
 * 实现用户登录、注册、刷新令牌等认证相关业务逻辑
 */
@Service
public class AuthServiceImpl implements AuthService {

    private static final Logger logger = LoggerFactory.getLogger(AuthServiceImpl.class);

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private UserService userService;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private JwtUtil jwtUtil;

    @Autowired
    private LoginAttemptService loginAttemptService;
    
    @Autowired
    private JavaMailSender mailSender;
    
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    
    @Autowired
    private UserMapper userMapper;
    
    @Autowired
    private CommunityMapper communityMapper;
    
    @Autowired
    private UserCommunityMapper userCommunityMapper;
    
    @Autowired
    private VerificationCodeService verificationCodeService;
    
    @Value("${spring.mail.username}")
    private String fromEmail;
    
    @Value("${security.code.expiration}")
    private int codeExpirationMinutes;
    
    private static final String CODE_PREFIX = "login:code:";
    private static final Pattern EMAIL_PATTERN = Pattern.compile("^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$");
    private static final Random RANDOM = new Random();

    /**
     * 用户登录逻辑（密码方式）
     * 1. 检查账号是否被锁定
     * 2. 验证用户名和密码
     * 3. 生成JWT令牌（支持记住登录）
     * 4. 记录登录成功
     * 5. 返回登录响应
     */
    @Override
    public LoginResponse login(LoginRequest loginRequest) {
        String username = loginRequest.getUsername();
        boolean rememberMe = loginRequest.getRememberMe() != null && loginRequest.getRememberMe();
        
        // 检查账号是否被锁定
        if (loginAttemptService.isLocked(username)) {
            long lockTime = loginAttemptService.getLockRemainingTime(username);
            throw new RuntimeException("账号已因多次登录失败被锁定，请 " + (lockTime / 60) + " 分钟后再试");
        }
        
        try {
            // 进行身份验证
            Authentication authentication = authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(
                            username,
                            loginRequest.getPassword()
                    )
            );

            SecurityContextHolder.getContext().setAuthentication(authentication);
            
            // 获取用户信息
            User user = userService.getUserByUsername(username);
            
            // 构建登录响应
            LoginResponse response = new LoginResponse();
            // 根据是否记住登录生成不同有效期的令牌
            response.setToken(jwtUtil.generateToken(user.getUserId(), user.getUsername(), rememberMe));
            response.setRefreshToken(jwtUtil.generateRefreshToken(user.getUserId(), user.getUsername()));
            response.setUserId(user.getUserId());
            response.setUsername(user.getUsername());
            response.setEmail(user.getEmail());
            response.setPhone(user.getPhone());
            response.setNickname(user.getNickname());
            response.setAvatar(user.getAvatar());
            response.setRole(user.getRole());
            response.setStatus(user.getStatus());
            response.setCreatedTime(user.getCreateTime());
            response.setLastLoginTime(LocalDateTime.now());
            
            // 更新用户最后登录时间
            user.setLastLoginTime(LocalDateTime.now());
            userService.updateUser(user);
            
            // 记录登录成功，清除失败尝试
            loginAttemptService.loginSucceeded(username);
            
            logger.info("用户登录成功: {}", username);
            return response;
        } catch (Exception e) {
            // 记录登录失败
            loginAttemptService.loginFailed(username);
            
            int remainingAttempts = loginAttemptService.getRemainingAttempts(username);
            if (remainingAttempts <= 0) {
                logger.warn("账号已被锁定: {}", username);
                throw new RuntimeException("账号已因多次登录失败被锁定，请15分钟后再试");
            }
            
            logger.warn("用户登录失败: {}, 剩余尝试次数: {}", username, remainingAttempts);
            throw new RuntimeException("账号或密码错误，请检查后重新输入");
        }
    }

    /**
     * 用户注册逻辑
     * 1. 检查用户名是否已存在
     * 2. 验证邮箱验证码
     * 3. 验证社区ID是否存在
     * 4. 创建新用户
     * 5. 建立用户与社区的关联
     * 6. 自动登录并返回令牌
     */
    @Override
    @Transactional
    public LoginResponse register(RegisterRequest registerRequest) {
        // 检查用户名是否已存在
        if (userService.getUserByUsername(registerRequest.getUsername()) != null) {
            throw new RuntimeException("用户名已存在");
        }
        
        // 检查邮箱是否已存在
        if (registerRequest.getEmail() != null && userService.existsByEmail(registerRequest.getEmail())) {
            throw new RuntimeException("邮箱已被注册");
        }
        
        // 检查手机号是否已存在
        if (registerRequest.getPhone() != null && userService.existsByPhone(registerRequest.getPhone())) {
            throw new RuntimeException("手机号已被注册");
        }
        
        // 验证邮箱验证码
        if (!verificationCodeService.verifyCode(registerRequest.getEmail(), registerRequest.getVerificationCode())) {
            throw new RuntimeException("验证码错误或已过期，请重新获取");
        }
        
        // 验证社区ID是否存在
        if (registerRequest.getCommunityId() == null || communityMapper.existsById(registerRequest.getCommunityId()) == 0) {
            throw new RuntimeException("选择的社区不存在");
        }
        
        // 创建新用户
        User user = new User();
        user.setUsername(registerRequest.getUsername());
        user.setPassword(passwordEncoder.encode(registerRequest.getPassword()));
        user.setEmail(registerRequest.getEmail());
        user.setPhone(registerRequest.getPhone());
        user.setNickname(registerRequest.getNickname() != null ? registerRequest.getNickname() : registerRequest.getUsername());
        user.setAvatar("default_avatar.png");
        user.setRole(1); // 1表示普通用户
        user.setStatus(1); // 1表示启用
        user.setCreateTime(LocalDateTime.now());
        user.setLastLoginTime(LocalDateTime.now());
        
        // 保存用户
        userService.registerUser(user);
        
        // 建立用户与社区的关联
        UserCommunity userCommunity = new UserCommunity();
        userCommunity.setUserId(user.getUserId());
        userCommunity.setCommunityId(registerRequest.getCommunityId());
        userCommunity.setJoinTime(LocalDateTime.now());
        userCommunity.setStatus(1); // 1表示正常状态
        
        int result = userCommunityMapper.insert(userCommunity);
        if (result != 1) {
            throw new RuntimeException("建立用户社区关联失败");
        }
        
        // 构建注册成功后的登录响应
        LoginRequest loginRequest = new LoginRequest();
        loginRequest.setUsername(registerRequest.getUsername());
        loginRequest.setPassword(registerRequest.getPassword());
        
        // 自动登录
        return login(loginRequest);
    }

    /**
     * 刷新令牌逻辑
     * 验证刷新令牌并生成新的访问令牌
     */
    @Override
    public LoginResponse refreshToken(TokenRefreshRequest tokenRefreshRequest) {
        // 从请求中获取刷新令牌
        String refreshToken = tokenRefreshRequest.getRefreshToken();
        
        // 验证刷新令牌的有效性
        if (refreshToken == null || refreshToken.isEmpty()) {
            throw new RuntimeException("刷新令牌不能为空");
        }
        
        // 使用JwtUtil解析刷新令牌获取用户信息
        if (!jwtUtil.validateToken(refreshToken)) {
            throw new RuntimeException("无效的刷新令牌");
        }
        
        // 从令牌中提取用户ID
        Long userId = jwtUtil.extractUserId(refreshToken);
        
        // 查询用户信息
        User user = userService.getUserById(userId);
        
        if (user == null) {
            throw new RuntimeException("无效的刷新令牌：用户不存在");
        }
        
        // 生成新的访问令牌和刷新令牌
        LoginResponse response = new LoginResponse();
        response.setToken(generateToken(user));
        response.setRefreshToken(jwtUtil.generateRefreshToken(user.getUserId(), user.getUsername()));
        response.setUserId(user.getUserId());
        response.setUsername(user.getUsername());
        response.setEmail(user.getEmail());
        response.setPhone(user.getPhone());
        response.setNickname(user.getNickname());
        response.setAvatar(user.getAvatar());
        response.setRole(user.getRole());
        response.setStatus(user.getStatus());
        
        return response;
    }

    /**
     * 生成JWT令牌
     * 使用项目中的JwtUtil生成
     */
    private String generateToken(User user) {
        return jwtUtil.generateToken(user.getUserId(), user.getUsername());
    }
    
    /**
     * 用户登录逻辑（验证码方式）
     * 1. 检查账号是否被锁定
     * 2. 验证邮箱格式和验证码的有效性
     * 3. 生成JWT令牌（支持记住登录）
     * 4. 记录登录成功
     * 5. 返回登录响应
     */
    @Override
    public LoginResponse codeLogin(CodeLoginRequest codeLoginRequest) {
        String email = codeLoginRequest.getEmail();
        String code = codeLoginRequest.getCode();
        boolean rememberMe = codeLoginRequest.getRememberMe() != null && codeLoginRequest.getRememberMe();
        
        // 检查邮箱格式
        if (!EMAIL_PATTERN.matcher(email).matches()) {
            throw new RuntimeException("邮箱格式不正确");
        }
        
        // 检查账号是否被锁定
        if (loginAttemptService.isLocked(email)) {
            long lockTime = loginAttemptService.getLockRemainingTime(email);
            throw new RuntimeException("账号已因多次登录失败被锁定，请 " + (lockTime / 60) + " 分钟后再试");
        }
        
        try {
            // 验证验证码
            if (!verifyLoginCode(email, code)) {
                // 记录登录失败
                loginAttemptService.loginFailed(email);
                
                int remainingAttempts = loginAttemptService.getRemainingAttempts(email);
                if (remainingAttempts <= 0) {
                    logger.warn("账号已被锁定: {}", email);
                    throw new RuntimeException("账号已因多次登录失败被锁定，请15分钟后再试");
                }
                
                throw new RuntimeException("验证码错误或已过期，请检查验证码或点击重新发送");
            }
            
            // 获取用户信息
            User user = userService.getUserByEmail(email);
            if (user == null) {
                throw new RuntimeException("该邮箱未注册账号");
            }
            
            // 构建登录响应
            LoginResponse response = new LoginResponse();
            // 根据是否记住登录生成不同有效期的令牌
            response.setToken(jwtUtil.generateToken(user.getUserId(), user.getUsername(), rememberMe));
            response.setRefreshToken(jwtUtil.generateRefreshToken(user.getUserId(), user.getUsername()));
            response.setUserId(user.getUserId());
            response.setUsername(user.getUsername());
            response.setEmail(user.getEmail());
            response.setPhone(user.getPhone());
            response.setNickname(user.getNickname());
            response.setAvatar(user.getAvatar());
            response.setRole(user.getRole());
            response.setStatus(user.getStatus());
            response.setCreatedTime(user.getCreateTime());
            response.setLastLoginTime(LocalDateTime.now());
            
            // 更新用户最后登录时间
            user.setLastLoginTime(LocalDateTime.now());
            userService.updateUser(user);
            
            // 记录登录成功，清除失败尝试
            loginAttemptService.loginSucceeded(email);
            
            // 清除已使用的验证码
            redisTemplate.delete(CODE_PREFIX + email);
            
            logger.info("用户验证码登录成功: {}", email);
            return response;
        } catch (RuntimeException e) {
            if (!e.getMessage().contains("验证码错误")) {
                logger.error("验证码登录异常: {}", e.getMessage());
            }
            throw e;
        }
    }
    
    /**
     * 发送登录验证码
     */
    @Override
    public ApiResponse<String> sendLoginVerificationCode(String email) {
        try {
            // 检查邮箱格式
            if (!EMAIL_PATTERN.matcher(email).matches()) {
                return ApiResponse.error("邮箱格式不正确");
            }
            
            // 检查邮箱是否已注册
            if (!userService.existsByEmail(email)) {
                return ApiResponse.error("该邮箱未注册账号");
            }
            
            // 检查是否在冷却期
            String coolDownKey = CODE_PREFIX + "cool:" + email;
            if (redisTemplate.hasKey(coolDownKey)) {
                long remainingTime = redisTemplate.getExpire(coolDownKey, TimeUnit.SECONDS);
                return ApiResponse.error("验证码发送过于频繁，请 " + remainingTime + " 秒后再试");
            }
            
            // 生成验证码
            String code = generateVerificationCode();
            
            // 存储验证码到Redis
            redisTemplate.opsForValue().set(CODE_PREFIX + email, code, codeExpirationMinutes, TimeUnit.MINUTES);
            
            // 设置发送冷却期（60秒）
            redisTemplate.opsForValue().set(coolDownKey, "", 60, TimeUnit.SECONDS);
            
            // 发送邮件
            sendVerificationCodeEmail(email, code);
            
            logger.info("登录验证码发送成功，邮箱：{}", email);
            return ApiResponse.success(null, "验证码已发送至您的邮箱，请注意查收（" + codeExpirationMinutes + "分钟内有效）");
        } catch (Exception e) {
            logger.error("发送登录验证码失败：{}", e.getMessage());
            return ApiResponse.error("发送验证码失败，请稍后重试");
        }
    }
    
    /**
     * 生成6位数字验证码
     */
    private String generateVerificationCode() {
        int code = 100000 + RANDOM.nextInt(900000);
        return String.valueOf(code);
    }
    
    /**
     * 验证登录验证码
     */
    private boolean verifyLoginCode(String email, String code) {
        if (email == null || code == null || code.length() != 6) {
            return false;
        }
        
        String key = CODE_PREFIX + email;
        String storedCode = redisTemplate.opsForValue().get(key);
        
        return storedCode != null && storedCode.equals(code);
    }
    
    /**
     * 发送验证码邮件
     */
    private void sendVerificationCodeEmail(String to, String code) throws MessagingException {
        MimeMessage message = mailSender.createMimeMessage();
        MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
        
        helper.setFrom(fromEmail);
        helper.setTo(to);
        helper.setSubject("登录验证码");
        
        String content = "<div style='font-family: Arial, sans-serif; padding: 20px;'>" +
                "<h2>您的登录验证码</h2>" +
                "<p>您正在尝试使用验证码登录PetDog系统，您的验证码是：</p>" +
                "<div style='font-size: 24px; font-weight: bold; margin: 20px 0; color: #333;'>" + code + "</div>" +
                "<p>验证码有效期为" + codeExpirationMinutes + "分钟，请尽快使用。</p>" +
                "<p>如果您没有尝试登录，请忽略此邮件。</p>" +
                "</div>";
        
        helper.setText(content, true);
        
        mailSender.send(message);
    }
    
    /**
     * 发送验证码
     * 1. 检查邮箱是否已被注册
     * 2. 调用验证码服务发送验证码
     */
    @Override
    public ApiResponse<String> sendVerificationCode(SendVerificationCodeRequest request) {
        try {
            // 检查邮箱是否已被注册
            if (userService.existsByEmail(request.getEmail())) {
                logger.warn("邮箱已被注册：{}", request.getEmail());
                return ApiResponse.error("该邮箱已被注册");
            }
            
            // 发送验证码
            verificationCodeService.sendVerificationCode(request.getEmail(), verificationCodeService.generateCode(), request.getType());
            
            logger.info("验证码发送成功，邮箱：{}", request.getEmail());
            return ApiResponse.success("验证码已发送至您的邮箱，请查收");
        } catch (RuntimeException e) {
            logger.error("发送验证码失败：{}", e.getMessage());
            return ApiResponse.error(e.getMessage());
        }
    }
}
